package com.cloud.zuul.filter;

import com.alibaba.fastjson.JSON;
import com.cloud.zuul.bean.SessionInfo;
import com.cloud.zuul.bean.ReleaseInfo;
import com.cloud.zuul.bean.ResponseInfo;
import com.cloud.zuul.cache.SessionCache;
import com.cloud.zuul.constant.SysConstant;
import com.cloud.zuul.context.VersionHolder;
import com.cloud.zuul.core.ConfigLoader;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Set;

@Slf4j
@Component
public class ContextFilter extends ZuulFilter {

    @Autowired
    private ConfigLoader configLoader;

    @Autowired
    private VersionHolder versionHolder;

    @Autowired
    private SessionCache sessionCache;

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public Object run() {
        versionHolder.remove();
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        if (context.get(FilterConstants.SERVICE_ID_KEY) == null) {
            return null;
        }

        //跨域请求初次调用直接通过
        if (request.getMethod().equalsIgnoreCase(RequestMethod.OPTIONS.toString())) {
            return null;
        }

        String requestURI = request.getRequestURI();
        if (!isExcludePath(requestURI)) {
            //静态资源
            if (isStaticResource(requestURI)) {
                String version = getVersionInfo(request);
                if (version != null) {
                    versionHolder.set(version);
                    return null;
                }
            }
            //获取令牌
            String token = getToken(request);
            if (token == null) {
                responseCode(context, ResponseInfo.UNAUTHORIZED);
                return null;
            }
            //检查令牌
            SessionInfo sessionInfo = sessionCache.check(token);
            if (sessionInfo == null) {
                responseCode(context, ResponseInfo.UNAUTHORIZED);
                return null;
            }
            //检查授权资源
            if (!sessionInfo.getAuthPaths().contains(requestURI)) {
                responseCode(context, ResponseInfo.FORBIDDEN);
                return null;
            }

            //获取版本并设置到上下文
            String version = getReleaseVersion(sessionInfo);
            versionHolder.set(version);

            //添加请求头
            sessionInfo.setAuthPaths(null);
            context.addZuulRequestHeader(SysConstant.TOKEN, token);
            context.addZuulRequestHeader(SysConstant.VERSION_INFO, version);
            context.addZuulRequestHeader(SysConstant.SESSION_INFO, JSON.toJSONString(sessionInfo));
        }
        return null;
    }

    /**
     * 输出结果
     * @param context
     * @param code
     */
    private void responseCode(RequestContext context, int code) {
        context.setSendZuulResponse(false);
        context.setResponseStatusCode(code);

        try {
            ResponseInfo responseInfo = new ResponseInfo(code);
            if (code == ResponseInfo.UNAUTHORIZED) {
                responseInfo.setMessage("会话过期，请重新登录");
            } else if (code == ResponseInfo.FORBIDDEN) {
                responseInfo.setMessage("权限不足，禁止访问");
            }

            HttpServletResponse response = context.getResponse();
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            response.getWriter().write(JSON.toJSONString(responseInfo));
        } catch (IOException e) {
        }
    }

    /**
     * 是否静态资源
     * @param requestURI
     * @return boolean
     */
    private boolean isStaticResource(String requestURI) {
        if (requestURI.contains(".")) {
            return true;
        }
        return false;
    }

    /**
     * 是否排除校验路径
     * @param requestURI
     * @return boolean
     */
    private boolean isExcludePath(String requestURI) {
        Set<String> excludePaths = configLoader.getExcludePaths();
        for (String path : excludePaths) {
            if (requestURI.startsWith(path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取发布版本
     * @param sessionInfo
     * @return
     */
    private String getReleaseVersion(SessionInfo sessionInfo) {
        List<ReleaseInfo> list = configLoader.getReleaseInfoList();
        for (ReleaseInfo releaseInfo : list) {
            String userId = releaseInfo.getUserId();
            String userArea = releaseInfo.getUserArea();
            if (userId.equals("*") || userArea.equals("*")) {
                return releaseInfo.getVersion();
            }
            if (userId.contains(sessionInfo.getUserId()) || userArea.contains(sessionInfo.getUserArea())) {
                return releaseInfo.getVersion();
            }
        }
        return null;
    }

    /**
     * 获取版本信息
     * @param request
     * @return String
     */
    private String getVersionInfo(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(SysConstant.VERSION_INFO)) {
                    return cookie.getValue();
                }
            }
        }
        return null;
    }

    /**
     * 获取令牌
     * @param request
     * @return String
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getParameter(SysConstant.TOKEN);
        token = token == null ? request.getHeader(SysConstant.TOKEN) : token;
        if (token == null) {
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                for (Cookie cookie : cookies) {
                    if (cookie.getName().equals(SysConstant.TOKEN)) {
                        token = cookie.getValue();
                        break;
                    }
                }
            }
        }
        return token;
    }

}
